import EasyXML
from EasyDebugger import *
import string
import re

DEBUG_CRUMBPARSE = getDebugFlag()
DEBUG_PATH = getDebugFlag()
DEBUG_MATH = getDebugFlag()
DEBUG_TEXT = getDebugFlag()

#addLevel(DEBUG_CRUMBPARSE + DEBUG_MATH + DEBUG_PATH + DEBUG_TEXT)

class CrumbParser:
    #open dictionaries
    def __init__(self):
        self.funcs = {}
        self.textfuncs = {}
        self.halt = 0
        
    #register a new parsed crumb type
    def registerFunc(self, name, function):
        self.funcs[name] = function
        
    #render a new special parsed phrase for parsed text
    def registerTextFunc(self, name, function):
        self.textfuncs[name] = function
        
    #parse a crumb
    def parseCrumb(self, node, force=False):
        debug(DEBUG_CRUMBPARSE, "Parsing: " + node._name)
        
        #if this is a special node, and we havent forced it to be normal, parse it as such.
        if node._name in self.funcs and force is False:
            return self.funcs[node._name](self, node)
        
        if force is True: #parse children normally
            self.parseList(node._children)
        
        ret = 1
        
        if hasattr(node, "count"):
            node.count = int(node.count)
            node.count -= 1
            if node.count <= 0:
                ret = 0
                
        #don't destroy self.
        return (ret, [])
    
    #parse a list of crumbs
    def parseList(self, list):
        i = 0
        #run through the list by numbers.
        #foreach doesn't like it when stuff is deleted, so keep track of a point ourselves.
        #when stuff is destroyed, the rest of the list moves down to fill in the empty spot.
        while(i < len(list)):
            #Make sure the element isn't empty
            if list[i] is None:
                del list[i]
                continue
            
            #parse this item
            result = self.parseCrumb(list[i])
            
            #check the result....
            if result[0] == 0:
                list.pop(i)    #if we want to be destroyed, do so.
            else:
                i += 1        #if not, continue along the array.
                
            #insert the results into the list. We'll retract them manually later if neccesary.
            for child in result[1]:
                list.insert(i, child)
                child._parent = list[i]._parent
                
    #parse in-game text
    def parseText(self, node, text, math=False):
        debug(DEBUG_TEXT, "Parsing text: " + text)
        #hack to make math expressions work correctly.
        if math:
            mathx = "'"
        else:
            mathx = ""
            
        ret = ''
        parse = ''
        level = 0
        caps = False
        
        for char in text:
            if char == '[': #begin a level
                level += 1
                if level > 1:
                    parse += char #if we're in a level, add it to the parse.
            elif level == 0:
                ret += char
            else:
                if char == '@' and level == 1: #we want caps on this level
                    caps = True
                elif char == ']': #end a level
                    level -= 1
                    if level == 0: #we're done with this parse, since we just ended the last level
                        #parse said section
                        out = self.parseFullPath(node, parse)
                        #hack to check true/false in math equations
                        if type(out) == type(False) and out is False: #parse bools
                            out = "False"
                        else:
                            try:            
                                float(out) #is it a number?
                                out = str(out)
                            except:
                                #if its a string, parse it as such
                                if type(out) == type('') or type(out) == type(u''):
                                    out = mathx + str(out) + mathx
                                else: #if not, just call it true and be done with it.
                                    out = "True"
                        ret += out
                        debug(DEBUG_TEXT, parse + " parsed as " + out)
                        #reset
                        caps = False
                        parse = ''
                    else:
                        parse += char #if this isnt the end, add it to the parse.
                else:
                    parse += char    
                        
                       

        #join the words back into text and return it.
        return ret
        
    #parse in-game text
    def parseTextDepreciated(self, node, text, math=False):
        debug(DEBUG_TEXT, "Parsing text: " + text)
        #hack to make math expressions work correctly.
        if math:
            mathx = "'"
        else:
            mathx = ""
            
        #split the text into words
        l = text.split(' ')
        
        #iterate the words...
        for i in range(0, len(l)):
            segs = l[i].split('`')
            for x in range(0, len(segs)):
                #make sure its not empty
                if segs[x] == '':
                    continue
                
                #special word!!!
                if segs[x][0] == "#" or segs[x][0] == "@":
                    #parse said section
                    out = self.parseTextSpecial(node, segs[x:], math)
                    
                    #hack to check true/false in math equations
                    if out == False and type(out) == type(False): #parse False
                        segs[x] = "False"
                    else:
                        try:            
                            float(out) #is it a number?
                            segs[x] = str(out)
                        except:
                            #try to parse strings...
                            if type(out) == type(u'') or type(out) == type(''):
                                if segs[x][0] == "@":
                                    out = string.capitolize(out)
                                segs[x] = mathx + out + mathx
                            else:
                                segs[x] = "True" #if all else fails, it just resolves to True.
            l[i] = ''.join(segs)
                            

        #join the words back into text and return it.
        return ' '.join(l)
    
    #parse text more
    def parseTextSpecial(self, node, list, math=False):
        #try parsing the full path, which will do math
        out = self.parseFullPath(node, list[0])
        
        #if False, return as such.
        if out == False:
            return False
        
        #if not, try to keep parsing.
        path = list[0].split('.')[0]
        if type(out) == type(''):
            out = self.parseText(self.parseFullPath(node, path), out, math)
            
        #it should only be 0 or 1. We hope.
        if len(list) > 1:
            out += list[1]
        return out
    
    #Parse a text path from a given location
    def parsePath(self, node, parse):
        debug(DEBUG_PATH, "Parsing path " + parse + " from " + node._name)
        
        #start here
        point = node
        
        id_x = re.compile(r'~[^:\.\)]*')
        func_x = re.compile(r'\$[^:\.\)]*')
        norm_x = re.compile(r'[^:\.\)\(\?\$~]*')
        
        done = False
        #loop through command steps
        while len(parse) > 0:
            debug(DEBUG_PATH, "Now Parsing: " + parse)
            if parse[0] == ':' or parse[0] == '.':
                parse = parse[1:]
                continue
            # a ~ indicates we want a specific child of the current point
            match = id_x.match(parse)
            if match:
                id = match.group()[1:] #knock off the ~
                parse = parse[match.end():]
                ok = 0
                for child in point._children: #try and find the child
                    if hasattr(child, "id") == True:
                        if child.id == id:
                            point = child
                            ok = 1
                            break
                if ok == 0: #we didnt find the right child.
                    debug(DEBUG_PATH, id + " id not found")
                    return False
                continue
            # a $ tries to utilize a registered text parse function, like $parent or $root
            match = func_x.match(parse)
            if match:
                func = match.group()[1:]
                parse = parse[match.end():]
                if func in self.textfuncs:
                    #find the text function and execute it
                    point = self.textfuncs[func](self, point)
                    if point == False:
                        debug(DEBUG_PATH, match.group() + " returned false")
                        return False
                else:
                    debug(DEBUG_PATH, match.group() + " textfunc not found")
                    return False
                continue
                
            # theres no special characters
            match = norm_x.match(parse)
            if match and match.group() != '':
                step = match.group()
                ok = 0
                for child in point._children:
                    if child._name == step:
                        ok = 1
                        point = child
                        parse = parse[match.end():]
                        break
                if ok == 0:
                    debug(DEBUG_PATH, step + " child not found")
                    return False
                else:
                    continue

            #?(expr) is an operator that matches any child with the given expr to be true
            if parse[0] == "?":
                expr = ""
                nest = 0
                #grab whatevers in the parens, watch for nests.
                for char in parse[1:]:
                    if char == '(':
                        if nest > 0:
                            expr += char
                        nest += 1
                    elif char == ')':
                        nest -= 1
                        if nest == 0:
                            break
                        else:
                            expr += char
                    else:
                        expr += char

                found = False
                for child in point._children:
                    if self.parseMath(child, expr):
                        point = child
                        found = True
                        break
                    
                if found is False:
                    debug(DEBUG_PATH, expr + " expr not found")
                    return False
                #hack off the part we just parsed.
                parse = parse[len(expr) + 3:]
                
        return point
    
    
    #Parse a text path from a given location
    def parsePathDepreciated(self, node, parse):
        debug(DEBUG_PATH, "Parsing path " + parse + " from " + node._name)
        
        #split into commands
        path = parse.split(':')
        #start here
        point = node
        
        #loop through command steps
        for step in path:
            
            #make sure theres actually a step/
            if len(step) > 0:
                # a ~ indicates we want a specific child of the current point
                if step[0] == '~':
                    id = step[1:] #knock off the ~
                    ok = 0
                    for child in point._children: #try and find the child
                        if hasattr(child, "id") == True:
                            if child.id == id:
                                point = child
                                ok = 1
                                break
                    if ok == 0: #we didnt find the right child.
                        debug(DEBUG_PATH, step + " failed")
                        return False
                # a $ tries to utilize a registered text parse function, like $parent or $root
                elif step[0] == "$":
                    if step[1:] in self.textfuncs:
                        #find the text function and execute it
                        point = self.textfuncs[step[1:]](self, point)
                        if point == False:
                            debug(DEBUG_PATH, step + " failed")
                            return False
                    else:
                        debug(DEBUG_PATH, step + " failed")
                        return False
                #?(expr) is an operator that matches any child with the given expr to be true
                elif step[0] == "?":
                    expr = step[2:len(step) - 1]
                    found = False
                    for child in point._children:
                        if self.parseMath(child, expr):
                            point = child
                            found = True
                            break
                        
                    if found is False:
                        debug(DEBUG_PATH, step + "failed")
                        return False
                    
                # if nothing else, we just want a child of the current node.
                else:
                    ok = 0
                    for child in point._children:
                        if child._name == step:
                            ok = 1
                            point = child
                            break
                    if ok == 0:
                        debug(DEBUG_PATH, step + " failed")
                        return False
        
        return point
    
    #parse a full path, including attributes
    def parseFullPath(self, node, parse):
                        
        rx = re.compile(r'\.[^\.\)]*$')
        attrs = rx.search(parse)
        
        if attrs:
            attr = attrs.group()[1:] #turn into string and remove the dot
            parse = parse[0:attrs.start()] #remove the attr and dot from the parse
            
        point = self.parsePath(node, parse)
        
        #is there a dot?
        if attrs:
            #try and get the attribute
            if hasattr(point, attr) is False:
                debug(DEBUG_PATH, attr + " var failed")
                return 0
            
            debug(DEBUG_PATH, "Successfully parsed to " + point._name + " and found " + attr)
            return getattr(point, attr)
                
        #if not, return the node.
        if point:
            debug(DEBUG_PATH, "Successfully parsed to " + point._name)
        else:
            debug(DEBUG_PATH, "Couldnt find " + parse)
        return point
                
    #tasty maths! Small wrapper to reformulate an expression.
    def parseMath(self, node, expr):
        #parse the text to get actual values.
        debug(DEBUG_MATH, "Parsing math " + expr)
        expr2 = self.parseText(node, expr, math=True)
        expr3 = eval(expr2)
        debug(DEBUG_MATH, "Expr " + expr + " becomes " + expr2 + " (" + str(expr3) + ")  in " + node._name)
        
        #evaluate the work
        return expr3